package sg.edu.nus.iss.cats.filters;

import java.io.IOException;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

import sg.edu.nus.iss.cats.controller.*;
import sg.edu.nus.iss.cats.model.*;
import sg.edu.nus.iss.cats.exception.*;
import sg.edu.nus.iss.cats.dataaccess.*;


/**
 * Implementation of <code>javax.servlet.Filter</code> used to check access rights
 *
 * @version $Revision: 1.0
 */
public class ResourceAccessFilter implements Filter {

    /**
     * The filter configuration object we are associated with.  If this value
     * is null, this filter instance is not currently configured.
     */
    private static final String RIGHTS_CONFIGPARAM = "rights";

	private static final String OWNER         = "OWNER";
	private static final String DIRECTMANAGER = "DIRECTMANAGER";
	private static final String MANAGER       = "MANAGER";
	private static final String ADMIN         = "ADMIN";

    private FilterConfig config = null;
    private String[]     configRights;

    private String       errorPage = null;

     /**
     * Place this filter into service.
     *
     * @param filterConfig The filter configuration object
     */

    public void init(FilterConfig filterConfig) {
        this.config = filterConfig;

        String rightsString = config.getInitParameter(RIGHTS_CONFIGPARAM);
        StringTokenizer st = new StringTokenizer(rightsString);
        configRights = new String [st.countTokens()];
        for (int i = 0; st.hasMoreTokens(); i++) {
            configRights[i] = st.nextToken().toUpperCase();
        }
        System.out.println("Found Rights: " + configRights.length);
        ServletContext appl = config.getServletContext();
        this.errorPage = appl.getInitParameter("errorPage");
    }

    /**
    * Take this filter out of service.
    */
    public void destroy() {
        this.config = null;

    }

    /**
     * The <code>doFilter</code> method of the Filter is called by the container
     * each time a request/response pair is passed through the chain due
     * to a client request for a resource at the end of the chain.
     * The FilterChain passed into this method allows the Filter to pass on the
     * request and response to the next entity in the chain.<p>
     * This method first checks if there is a valid user session <br>
     * It also checks if the access rights provided for the user allows access to the resource<br>
     **/
    public void doFilter ( ServletRequest request, ServletResponse response,
                        FilterChain chain ) throws IOException, ServletException {

        System.out.println("ResourceAccessFilter: doFilter");
        try{

            HttpSession session = ((HttpServletRequest)request).getSession(false);
            UserSession userSession = (UserSession) session.getAttribute("USERSESSION");
            User user = userSession.getUser();

            String courseId = request.getParameter("course_id");
            if ((courseId != null) && (courseId.length() != 0)) {
                System.out.println("ResourceAccessFilter: check for course access: " + courseId);
                CourseDAO courseDAO = DAOFactory.getInstance().getCourseDAO();
                Course course = courseDAO.findCourse(courseId);
                String ownerId = course.getEmployeeId();
                checkRights (user, ownerId);
            }

            String employeeId = request.getParameter("employee_id");
            if ((employeeId != null) && (employeeId.length() != 0)) {
                System.out.println("ResourceAccessFilter: check for employee access: " + employeeId);
                checkRights (user, employeeId);
            }
            System.out.println("ResourceAccessFilter: check successful");

            chain.doFilter( request, response );

        } catch (NoAccessException e)  {
            System.out.println("ResourceAccessFilter: No access");
            request.setAttribute ("CATS_ERROR_PAGE_TITLE", "title.noAccess");
            request.setAttribute ("CATS_ERROR_PAGE_MESSAGE", "error.noAccess");
            RequestDispatcher rd = config.getServletContext().getRequestDispatcher(errorPage);
            rd.forward(request, response);
            return;

        } catch (Exception e) {
            System.out.println("ResourceAccessFilter: Exception: " + e);
            RequestDispatcher rd = config.getServletContext().getRequestDispatcher(errorPage);
            rd.forward(request, response);
            return;
        }
	}


    private void checkRights (User user, String subjectId)
                              throws NoAccessException, DAOException {
        for (int i = 0; i < configRights.length; i++) {
            String rightTested = configRights[i];
            if (hasRight (rightTested, user, subjectId)) {
                System.out.println("ResourceAccessFilter: successful for right " + rightTested);
                return;
			}
        }
        throw new NoAccessException ();
    }


    private boolean hasRight (String rightTested, User user, String subjectId)
							throws NoAccessException, DAOException {

        if (ADMIN.equals(rightTested)) {
            return (user.hasRole("admin"));
        }

        // If there is no subject to check against, assume the rights are granted
        if (subjectId == null) {
            System.out.println("ResourceAccessFilter: no subject id ");
            return true;
        }

        // If not an admin, User must be an employee
        // Get User's employee id
        String userEmployeeId = user.getEmployeeId();
        System.out.println("userEmployeeId: " + userEmployeeId);
        if (userEmployeeId == null) {
            System.out.println("ResourceAccessFilter: no employee id for user");
            return false;
        }

        // Perform the OWNER test
        if (OWNER.equals(rightTested)) {
            return (subjectId.equals(userEmployeeId));
        }

        // Get the subject's employee record
        EmployeeDAO empDAO = DAOFactory.getInstance().getEmployeeDAO ();
        Employee subject = empDAO.findEmployee(subjectId);
        System.out.println("owner: " + subject);
        if (subject == null) {
            System.out.println("ResourceAccessFilter: no subject");
            return false;
        }

        // Traverse the management chain
        Employee emp = subject;
        while (emp != null) {
            String managerId = emp.getManagerId();
            System.out.println("managerId: " + managerId +ResourceAccessFilter.MANAGER);
            if (managerId == null) {
                System.out.println("ResourceAccessFilter: no manager");
                return false;
            }

            if (managerId.equals(userEmployeeId)) {
                System.out.println("ResourceAccessFilter: found manager!");
                return true;
            }

            // If we're here, this cannot be a direct manager
            if (DIRECTMANAGER.equals(rightTested)) {
                System.out.println("ResourceAccessFilter: not a direct manager");
                return false;
            }

            // Move up the chain to the manager
            emp = empDAO.findEmployee (managerId);
            System.out.println("manager: " + emp);
        }
        System.out.println("ResourceAccessFilter: reached top of manager hierarchy");
        return false;
    }
}

